using System;
using Microsoft.Xna.Framework;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;

namespace Rogue
{
    #region body setup
    class WolfMouth : BodyPart
    {
        public WolfMouth(BodyPart a_parent)
            : base(a_parent)
        {
            m_Health = m_MaxHealth = 10;
        }
    };
    #endregion

    [Serializable]
    public class Wolf : Entity
    {
        bool m_bStunned = false;

        public Wolf()
            : base()
        {
            AddWolfBody();
        }

        static Attack s_BiteAttack = new Attack("bite", "bites", "mouth", 3, DamageType.Cut, Attack.Priority.Normal);

        private void AddWolfBody()
        {
            m_BodyRoot = new Torso(this);
            Head head = new Head(m_BodyRoot);
            WolfMouth mouth = new WolfMouth(head);
            mouth.GrantedAttack = s_BiteAttack;

            new Leg(m_BodyRoot, "front left");
            new Leg(m_BodyRoot, "front right");
            new Leg(m_BodyRoot, "rear left");
            new Leg(m_BodyRoot, "rear right");

            m_BodyRoot.MultMaxHealth(0.8f);

            m_BodyRoot.OnSubPartLoss += SubPartLossHandler;
            m_BodyRoot.OnSubPartHealthChange += BodyHealthChange;
            head.OnSubPartHealthChange += HeadHealthChange;

            m_BodyRoot.InitAttackIndices();
        }

        // this is what can kill us.
        private void SubPartLossHandler(BodyPart a_Part)
        {
            if (a_Part.GetType() == typeof(Head) || a_Part.GetType() == typeof(Torso))
            {
                m_IsAlive = false;
            }

            a_Part.MakeSeveredPart(this);
        }

        void HeadHealthChange(BodyPart a_Part, BodyPart.HealthChangeType a_Type, int a_DeltaHealth)
        {
            if (a_DeltaHealth < 0 && !m_bStunned)
            {
                m_bStunned = true;
                ConsoleWindow.Instance.AddPostDamageMessage(String.Format("{0} is stunned.", Name), Color.SandyBrown);
            }
        }

        void BodyHealthChange(BodyPart a_Part, BodyPart.HealthChangeType a_Type, int a_DeltaHealth)
        {
            if (a_Type == BodyPart.HealthChangeType.Bleed)
            {
                RogueGame.Instance.Map.BloodyTerrain(X, Y, true);
            }
        }

        static DetailWindowImages s_WolfDetailWindowImages = new DetailWindowImages("wolf");
        public override DetailWindowImages GetDetailWindowImages()
        {
            return s_WolfDetailWindowImages;
        }

        public override int GetMoveDelay()
        {
            int healthyLegs = 0;
            foreach (Leg leg in m_BodyRoot.GetAllParts<Leg>())
            {
                if (leg.GetTotalHealth() > leg.GetTotalMaxHealth(true) / 2)
                    ++healthyLegs;
            }

            switch (healthyLegs)
            {
                case 3: return m_nDelay * 2;
                case 2: return m_nDelay * 4;
                
                case 1: 
                case 0:
                    return m_nDelay * 10;
            }

            // 4 or more legs
            return Math.Max(1, m_nDelay * 4 / healthyLegs);
        }

        public override string GetStatusDescription()
        {
            string wolfStatus = "aggressive";
            if (!m_bBrave)
                wolfStatus = "afraid";
            if (m_bStunned)
                wolfStatus = "stunned";

            string speedString = DescribeMoveSpeed();
            if (speedString != null)
                speedString += ", ";

            return speedString + wolfStatus;
        }

        // 0-100 measure of whether he'll run away or attack
        int m_Bravery = 80;
        bool m_bBrave = false;

        // super hacky pack AI. These wolves are dumb!
        public override void Think()
        {
            Debug.Assert(IsAlive);

            Entity hero = RogueGame.Instance.Map.GetHero();

            int dx = hero.X - X;
            int dy = hero.Y - Y;

            const float attackRadius = 5.0f;
            const int meetupRadius = 5;

            // once we have some buddies in the attack radius, we move in. It's scary if we're alone, though!
            m_Bravery -= 20;
            foreach (Wolf w in Entity.Collector.GetAllWithin<Wolf>(hero.Position, attackRadius))
            {
                if (w.IsAlive)
                    m_Bravery += 20;
            }

            if (!m_BodyRoot.HasParts<WolfMouth>())  // can't attack
                m_Bravery = m_Bravery / 2;

            if (m_BodyRoot.GetHealth() < 20 || m_BodyRoot.GetTotalHealth() < 40)    // hurt
                m_Bravery -= 10;

            if (dx * dx + dy * dy > meetupRadius * meetupRadius)    // he's far away, let's meet up up near him
            {
                m_Bravery += 50;
            }

            if (!hero.IsAlive || m_bStunned)
            {
                dx = Random.Instance.Next(-1, 2);
                dy = Random.Instance.Next(-1, 2);
            }
            else if (m_Bravery < 30)  // oh fuuuuck
            {
                if (m_bBrave)
                {
                    LabelHelper.Instance.Add("*whimper*", this, 1.0f);
                }

                m_bBrave = false;
            }

            m_Bravery = Math.Min(100, Math.Max(0, m_Bravery));

            if (!m_bBrave && m_Bravery >= 100 && RogueGame.Instance.Map.LightCaster.IsVisible(Position))    // became brave when visible
            {
                m_bBrave = true;
                LabelHelper.Instance.Add("grr!", this, 1.0f);
            }

            if (!m_bBrave)
            {
                dx *= -1;
                dy *= -1;
            }

            if (m_bStunned)
            {
                // should chance of becoming unstunned each turn depend on head damage?
                bool bleedyHead = false;
                foreach (Head h in m_BodyRoot.GetAllParts<Head>())
                {
                    if (h.BleedingLeft() > 0.0f)
                        bleedyHead = true;
                }

                if (Random.Instance.Next() < 0.3f && !bleedyHead)
                {
                    m_bStunned = false;
                    ConsoleWindow.Instance.AddMessage(String.Format("{0} is no longer stunned.", Name), Color.SandyBrown);
                }

                // will attack other wolves...
                Direction dir = Direction.GetDirection(dx, dy);
                if (dir != null)
                    MoveOrAttack(dir);
            }
            else if (m_Bravery >= 100 && hero.IsAlive &&
                Math.Abs(dx) <= 1 && Math.Abs(dy) <= 1)    // close enough to attack?
            {
                MeleeAttack(hero);
            }
            else
            {
                // hacky until we get pathfinding. This should get wolves to dodge around trees OK
                int sx = Math.Sign(dx);
                int sy = Math.Sign(dy);
                Direction[] tryMoving = {
                    Direction.GetDirection(sx, sy),
                    Direction.GetDirection(sx+1, sy),
                    Direction.GetDirection(sx, sy+1),
                    Direction.GetDirection(sx-1, sy),
                    Direction.GetDirection(sx, sy-1),
                };

                foreach(Direction d in tryMoving)
                {
                    if (d == null)  // if it's not a valid direction
                        continue;

                    // we're free to move there
                    Point newPos = Position + d;

                    // do any entities block us?
                    bool breakOuter = false;
                    foreach (Entity e in Entity.Collector.GetAllAt<Entity>(newPos.X, newPos.Y))
                    {
                        if (e.BlocksActor(this))
                        {
                            breakOuter = true;
                        }
                    }
                    if (breakOuter)
                        break;

                    // does the terrain block us?
                    if (!RogueGame.Instance.Map.BlocksActor(newPos.X, newPos.Y))
                    {
                        MoveDelta(d);
                        break;
                    }
                }

                // cornered - desperately fight!
                if (hero.IsAlive &&
                Math.Abs(dx) <= 1 && Math.Abs(dy) <= 1)    // close enough to attack?
                {
                    MeleeAttack(hero);
                }
            }

            // call this here in case we didn't pick another action.
            if (m_nNextThink == Timeline.Instance.CurrentTime)
                EndTurn();

            bool wasAlive = IsAlive;

            // this is kind of weird, we're pre-healing.
            // It should be m_nNextThink - m_nLastThink...
            int elapsedTime = m_nNextThink - Timeline.Instance.CurrentTime;
            m_BodyRoot.UpdateSubpartsHealth(elapsedTime);

            if (wasAlive && !IsAlive)
            {
                // TODO: have the battle reporter thing handle this
                ConsoleWindow.Instance.AddMessage(String.Format("{0} has bled to death.", Name), Color.Red);
                Die();
            }
        }

        #region Serialization
        public Wolf(SerializationInfo info, StreamingContext context) : base(info, context)
        {
            // Overridden
        }
        #endregion
    }
}


